<?php
/**
 * Created by PhpStorm.
 * User: George
 * Date: 2017/9/23
 * Time: 20:34
 */

namespace AdminBundle\Task\Scheduler;


use AdminBundle\Entity\TaskJobHistory;
use AdminBundle\Task\TaskFactory;
use AdminBundle\Entity\TaskQueueWaiting;

class Job
{
    private $em = null;
    private $jobs = null;
    private $jobCurrent = null;
    private $debug = false;

    public function __construct($em, $debug = false)
    {
        $this->em = $em;
        $this->debug = $debug;
    }


    /**
     * 获取当前可运行的作业
     * @return mixed
     */
    public function begin()
    {
        $runnableJobs = $this->em->getRepository('AdminBundle:TaskJobSchedules')->runnableJobs();
        $this->jobs = $runnableJobs;
        return count($runnableJobs);
    }

    /**
     * 生成作业下次的调度时间
     * @param $jobId
     * @return int
     */
    public function nextSchedule($duration)
    {
        return $this->em->getRepository('AdminBundle:TaskJobSchedules')->nextSchedule($duration);
    }

    /**
     * 作业完成后,调度队列中的作业ID删除掉
     * @param $jobId
     * @return mixed
     */
    public function finishScheduleJob($jobId)
    {
        return $this->em->getRepository('AdminBundle:TaskJobSchedules')->finishScheduleJob($jobId);
    }

    /**
     * 查看当前可运行的作业
     * @return mixed
     */
    public function get()
    {
        return $this->jobs;
    }

    /**
     * 执行可运行的作业
     * @return int
     */
    public function execute()
    {
        $count = 0;
        if ($this->jobs == null) {
            return $count;
        }

        foreach ($this->jobs as $job) {
            $this->jobCurrent = &$job;

            $duration = microtime(true);

            $taskJobHistory = new TaskJobHistory();
            $taskJobHistory->setJobId($job['jobId'])
                ->setStepId(0)
                ->setStepName('运行结果')
                ->setRunDate(new \DateTime())
                ->setRunTime(new \DateTime())
                ->setRunStatus(0)
                ->setServerName('serverJob');

            $countForJob = $this->execJobs($job);
            $count += $countForJob;

            $duration = microtime(true) - $duration;
            $taskJobHistory->setRunDuration($duration * 1000)
                ->setMessage(sprintf('共执行了 %d 次,耗时 %d 秒.',
                    $countForJob, $duration));

            $this->em->persist($taskJobHistory);
            $this->em->flush();

            // 完成作业调度时间
            if (1) // todo: 线上时把此行删除掉
                $this->finishScheduleJob($job['jobId']);
        }
        return $count;
    }

    /**
     * 根据任务获取其对应步骤,按按步骤顺序执行
     * @param $job
     * @return int
     */
    private function execJobs(&$job)
    {
        $count = 0;
        // 获取作业对应的模版数据
        $steps = $this->em->getRepository('AdminBundle:TaskJobs')->stepByJob(['job_id' => $job['jobId']]);
        if ($steps == null) {
            return $count;
        }
        // 获取设备
        $devices = $this->em->getRepository('AdminBundle:BaseDevice')->deviceByGroup(['group_id' => $job['groupId']]);
        if ($devices == null) {
            return $count;
        }
        // 按作业步骤循环
        foreach ($steps as $step) {
            $countForStep = 0;

            $taskJobHistory = new TaskJobHistory();
            $taskJobHistory->setJobId($job['jobId'])
                ->setStepId($step['stepId'])
                ->setStepName($step['name'])
                ->setRunDate(new \DateTime())
                ->setRunTime(new \DateTime())
                ->setRunStatus(1)
                ->setServerName('serverJob');

            $duration = microtime(true);
            foreach ($devices as $device) {
                $countForStep += $this->execStep($step, $device);
            }

            $duration = microtime(true) - $duration;
            $taskJobHistory->setRunDuration($duration * 1000)
                ->setMessage(sprintf('%s: 设备数量 %d 台,共执行了 %d 次,耗时 %d 秒.',
                    $step['uri'], count($devices), $countForStep, $duration));

            $this->em->persist($taskJobHistory);
            $this->em->flush();

            unset($taskJobHistory);

            $count += $countForStep;
        }
        return $count;
    }

    /**
     * 根据作业来执行分配任务
     * @param $step
     * @param $device
     * @return int
     */
    private function execStep(&$step, &$device)
    {
        $wechats = $this->em->getRepository('AdminBundle:BaseWebchatUser')->wechatByDevice(['device_id' => $device['id']]);
        if (empty($wechats)) {
            return 0;
        }
        $taskQueueWaiting = new TaskQueueWaiting();
        $taskQueueWaiting->setCreator($this->jobCurrent['creator'])
            ->setIp('127.0.0.1')
            ->setAction($this->jobCurrent['token'])
            ->setDeviceGroupId($this->jobCurrent['groupId'])
            ->setDeviceId($device['id'])
            ->setDeviceName($device['name'] . ',' . $device['imei'])
            ->setName($step['name'])
            ->setTemplateId($step['templateId'])
            ->setUri($step['uri']);

        $taskQueue = TaskFactory::create($taskQueueWaiting, $this->debug);
        $count = 0;

        foreach ($wechats as $wechat) {
            // 根据随机参数判断是否执行当前步骤
            if ($this->isSkipped($step['isRand'])) {
                continue;
            }

            $taskQueueWaiting->setMethod($wechat['id'])
                ->setNote($wechat['nickname'])
                ->setWebchatUsername($wechat['userName']);

            $taskQueue->setTask($taskQueueWaiting);
            $taskQueue->setDoctrineManager($this->em);

            $loops = intval($step['recurrenceFactor']);
            for ($i = 0; $i < $loops; $i++) {
                // 任务核心处理,返回内容给微信
                $jsonTask = $taskQueue->jsonTask();

                if ($jsonTask == null) {
                    continue;
                }
                if ($this->debug) {
                    dump('', $jsonTask);
                }

                $clone_obj = clone $taskQueueWaiting;
                $clone_obj->setCtime(new \DateTime())
                    ->setParams($jsonTask);
                $this->em->persist($clone_obj);

                unset($clone_obj);
                $count++;
            }
            $this->em->flush();
        }
        unset($taskQueueWaiting, $taskQueue);

        return $count;
    }

    /**
     * 当isRand = 1 时;表示随机模式启动.取奇偶
     * @param $isRand 是否随机,
     * @return bool
     */
    private function isSkipped($isRand)
    {
        if ($isRand == "1")
            return mt_rand(1, 10) % 2 ? true : false;
        return false;
    }
}